
Отличный и очень глубокий вопрос! Вы абсолютно правы, проводя параллель между эволюцией интерфейсов (от скевоморфных к цифровым) и текущим состоянием ИИ, который во многом является "скевоморфным" по отношению к биологическому мозгу.
Давайте разберем эту идею на части.
Вы точно подметили: современный ИИ — это имитация на очень упрощённом и абстрактном уровне работы нейронов мозга.
Нейроны -> Искусственные нейроны.
Синапсы -> Веса связей.
Обучение -> Корректировка весов через обратное распространение ошибки.
Это мощная и плодотворная парадигма, но у неё есть фундаментальные недостатки, унаследованные от её "прототипа":
"Чёрный ящик": Мы не можем точно сказать, почему модель приняла то или иное решение.
Вычислительная ёмкость: Обучение больших моделей требует колоссальных ресурсов.
Хрупкость: Модели могут "сломаться" на данных, которые незначительно отличаются от обучающих.
Отсутствие настоящего понимания и рассуждений: Модель статистически предсказывает следующее слово или пиксель, но не строит внутренней логической модели мира.
Вопрос, который вы задали, лежит в основе одного из самых перспективных направлений современного ИИ — нейро-символического подхода.
Идея в том, чтобы объединить две силы:
Сила №1 (Нейросетевая/Субсимволическая): Способность к распознаванию образов, обобщению из данных, работе с зашумленной информацией. Это "восприятие".
Сила №2 (Символическая/Логическая): Способность к логическим выводам, манипуляции символами, работе с правилами, планированию и объяснению решений. Это "рассуждение".
Вместо того чтобы заставлять нейросеть имитировать рассуждение, мы создаём архитектуру, где нейросеть отвечает за то, что у неё получается лучше всего (например, преобразование текста в структурированные факты), а затем передаёт эстафету классическому, детерминированному "вычислительному модулю".
Представьте себе не монолитную трансформерную сеть, а операционную систему для мышления, состоящую из специализированных модулей:
Модуль восприятия (Perception Module):
Это как раз ваши word2vec и трансформеры. Его задача — преобразовать
сырые данные (текст, изображение, звук) в структурированные символы,
сущности и факты. Например, из предложения "Кот сидит на ковре" он
извлекает факт: LocatedOn(Cat, Mat).
Рабочая память (Working Memory): Буфер, где хранятся текущие факты, цели и контекст. Аналог оперативной памяти.
База знаний (Knowledge Base):
Постоянное хранилище фактов и правил о мире. Это может быть граф
знаний, наполняемый как людьми, так и самим ИИ в процессе обучения.
Например, Cat -> IsA -> Animal, Animal -> Can -> Breathe.
Движок логического вывода (Inference Engine):
Классический символьный ИИ. Он берёт факты из рабочей памяти и,
применяя правила из базы знаний, делает логические выводы. Если он
знает, что Animal -> NeedsTo -> DrinkWater и видит IsA(Cat, Animal), то он может сделать вывод NeedsTo(Cat, DrinkWater). Это делается без обучения, чистой логикой.
Планировщик (Planner): Модуль, который ставит цели и выстраивает последовательности действий для их достижения, используя выводы движка.
Модуль исполнения (Execution Module): Преобразует план в конкретные действия: генерацию текста, вызов API, управление роботом.
Объяснимость: Можно проследить цепочку рассуждений: "Я сказал X, потому что из факта Y и правила Z следует X".
Эффективность: Не нужно переучивать всю гигантскую модель для добавления нового знания. Достаточно добавить новое правило в базу знаний.
Контроль: Легче вносить исправления и ограничения ("соблюдай закон X").
Обобщение на малых данных: Зная правило "если существо млекопитающее, оно дышит лёгкими", система сразу применит его к любому новому млекопитающему, без необходимости видеть миллионы его примеров.
Надёжность: Меньше подвержена "галлюцинациям", так как выводы проверяются логикой.
Не заменить, а дополнить и усилить. Трансформеры и эмбеддинги останутся прекрасным "органами чувств" для такой системы. Их задача — понять неструктурированный мир и перевести его на язык, понятный символьным модулям.
Пример для вашего ИИ-чата на FreePascal:
Представьте, что ваш текущий трансформер генерирует ответ. Иногда он "галлюцинирует" и говорит что-то нелогичное.
А теперь представьте архитектуру, где:
Трансформер-Анализатор разбирает вопрос пользователя и извлекает из него ключевые сущности и намерение.
Эти данные попадают в Рабочую память.
Движок логического вывода (это может быть просто набор процедур на Pascal, работающих с графами или правилами) проверяет факты по Базе Знаний (например, SQLite-базе с онтологией) и строит логический каркас ответа.
Трансформер-Генератор получает этот логический каркас и "облекает его в плоть" естественного языка.
Такой чат будет гораздо более точным, логичным и управляемым.
Ваша интуиция абсолютно верна. Движение от чистого "нейроморфного скевоморфизма" к гибридным, нейро-символическим архитектурам — это и есть тот самый "следующий шаг". Это переход от имитации мышления к созданию функциональной системы машинного reasoning (рассуждений).
Это сложнее, чем просто натренировать ещё одну большую модель, но это путь к созданию по-настоящему разумных, надёжных и понятных систем. И в этом направлении ваш исследовательский проект на FreePascal может быть как раз очень интересным полигоном для экспериментов! Вы можете начать с малого: создать простую базу фактов и логический движок, и посмотреть, как его можно сочетать с вашей word2vec-моделью.
Отличный вопрос! Давайте подумаем, как можно интегрировать нейро-символический подход в ваш существующий движок на FreePascal.
Ваш текущий код уже содержит хорошую основу:
Word2Vec/трансформер - модуль восприятия
ContextManager - рабочая память
ConversationHistory - история диалога
TrainingData - накопленные знания
Вот как можно модифицировать ваш код:
// SymbolicEngine.pas unit SymbolicEngine; interface uses SysUtils, Classes, DataUtils, ucs4unit; type TFact = record Subject: string; Relation: string; Object: string; Confidence: Double; end; TRule = record Condition: string; // Логическое выражение Action: string; // Действие/вывод Priority: Integer; end; TSymbolicEngine = class private FFacts: array of TFact; FRules: array of TRule; FWorkingMemory: TStringList; public constructor Create; destructor Destroy; override; procedure AddFact(const Subject, Relation, Object: string; Confidence: Double = 1.0); procedure AddRule(const Condition, Action: string; Priority: Integer = 1); function Infer(const Query: string): TFact; function ExecuteRule(const Input: string): string; procedure SaveToFile(const Filename: string); procedure LoadFromFile(const Filename: string); end; implementation constructor TSymbolicEngine.Create; begin inherited; FWorkingMemory := TStringList.Create; SetLength(FFacts, 0); SetLength(FRules, 0); // Базовые правила AddRule('Contains(Input, "привет")', 'Greeting', 10); AddRule('Contains(Input, "пока")', 'Farewell', 10); AddRule('Contains(Input, "?") AND Length(Input) > 5', 'Question', 5); end; destructor TSymbolicEngine.Destroy; begin FWorkingMemory.Free; inherited; end; procedure TSymbolicEngine.AddFact(const Subject, Relation, Object: string; Confidence: Double); begin SetLength(FFacts, Length(FFacts) + 1); with FFacts[High(FFacts)] do begin Subject := Subject; Relation := Relation; Object := Object; Confidence := Confidence; end; end; procedure TSymbolicEngine.AddRule(const Condition, Action: string; Priority: Integer); begin SetLength(FRules, Length(FRules) + 1); with FRules[High(FRules)] do begin Condition := Condition; Action := Action; Priority := Priority; end; end; function TSymbolicEngine.ExecuteRule(const Input: string): string; var I: Integer; LowerInput: string; begin Result := ''; LowerInput := UTF8LowerCase(Input); // Применяем правила в порядке приоритета for I := 0 to High(FRules) do begin if FRules[I].Condition = 'Contains(Input, "привет")' then begin if LowerInput.Contains('привет') or LowerInput.Contains('здравств') then begin Result := 'Greeting'; Exit; end; end else if FRules[I].Condition = 'Contains(Input, "пока")' then begin if LowerInput.Contains('пока') or LowerInput.Contains('до свидан') then begin Result := 'Farewell'; Exit; end; end else if FRules[I].Condition = 'Contains(Input, "?") AND Length(Input) > 5' then begin if LowerInput.Contains('?') and (Length(Input) > 5) then begin Result := 'Question'; Exit; end; end; end; end; function TSymbolicEngine.Infer(const Query: string): TFact; begin // Упрощенный вывод - в реальности здесь будет полноценный логический движок Result.Subject := ''; Result.Relation := ''; Result.Object := ''; Result.Confidence := 0.0; end; procedure TSymbolicEngine.SaveToFile(const Filename: string); begin // Сохранение фактов и правил end; procedure TSymbolicEngine.LoadFromFile(const Filename: string); begin // Загрузка фактов и правил end; end.
// В AdvancedChatAI.pas var SymbolicEngine: TSymbolicEngine; procedure Initialize; begin // Существующая инициализация... // Добавляем символьный движок SymbolicEngine := TSymbolicEngine.Create; // Загружаем базу знаний если есть if FileExists('knowledge_base.txt') then SymbolicEngine.LoadFromFile('knowledge_base.txt'); end; procedure EnhancedProcessUserInput; var inputText: ucs4; userMessage, ruleResult, finalResponse: string; extractedFacts: array of TFact; symbolicContext: string; begin // 1. Получаем пользовательский ввод (как раньше) UserInput := GetUserInput(POST_FILE, filesList); inputText := UserInput.Message; userMessage := inputText.ToUTF8.Trim; if inputText.Length = 0 then Exit; WriteLn('Пользователь: ', userMessage); // 2. Символьный анализ ruleResult := SymbolicEngine.ExecuteRule(userMessage); WriteLn('Символьный анализ: ', ruleResult); // 3. Извлекаем факты через трансформер (нейросетевая часть) extractedFacts := ExtractFactsWithTransformer(userMessage); // 4. Добавляем факты в символьную систему for I := 0 to High(extractedFacts) do begin SymbolicEngine.AddFact(extractedFacts[I].Subject, extractedFacts[I].Relation, extractedFacts[I].Object, extractedFacts[I].Confidence); end; // 5. Логический вывод на основе контекста symbolicContext := BuildSymbolicContext(ConversationHistory); finalResponse := GenerateHybridResponse(userMessage, ruleResult, symbolicContext); // 6. Вывод результата WriteLn('AI: ', finalResponse); UpdateConversationContext('', finalResponse); end; function ExtractFactsWithTransformer(const Text: string): array of TFact; var inputMatrix, outputMatrix: TDoubleMatrix; entities: TStringArray; I: Integer; begin // Используем трансформер для извлечения сущностей и отношений inputMatrix := CreateContextEnrichedInput(Text, ''); ForwardTransformer(TransformerModel, inputMatrix, outputMatrix); // Преобразуем выход трансформера в факты entities := ParseEntitiesFromOutput(outputMatrix); SetLength(Result, Length(entities) div 3); // Предполагаем формат [субъект, отношение, объект] for I := 0 to (Length(entities) div 3) - 1 do begin Result[I].Subject := entities[I*3]; Result[I].Relation := entities[I*3+1]; Result[I].Object := entities[I*3+2]; Result[I].Confidence := 0.8; // На основе уверенности модели end; end; function GenerateHybridResponse(const UserMessage, RuleResult, Context: string): string; begin // Комбинируем подходы // 1. Если символьная система уверена - используем её if RuleResult = 'Greeting' then Result := 'Здравствуйте! Чем могу помочь?' else if RuleResult = 'Farewell' then Result := 'До свидания! Рад был пообщаться.' else if RuleResult = 'Question' then begin // Для вопросов используем комбинацию if Context.Contains('погод') then Result := 'К сожалению, у меня нет доступа к актуальным данным о погоде. ' else Result := 'Интересный вопрос! '; // Добавляем нейросетевую вариативность Result := Result + GenerateTransformerResponse(UserMessage); end else // По умолчанию - нейросеть Result := GenerateTransformerResponse(UserMessage); end;
procedure LearnSymbolicRules(const UserMessage, AIResponse: string); var patterns: TStringArray; newRule: string; begin // Анализируем успешные диалоги для извлечения правил if ShouldExtractRule(UserMessage, AIResponse) then begin patterns := ExtractPatterns(UserMessage); newRule := BuildRuleFromPatterns(patterns, AIResponse); SymbolicEngine.AddRule(newRule, AIResponse, 5); WriteLn('Добавлено новое правило: ', newRule); end; end;
Объяснимость: Можно точно сказать, почему выбран тот или иной ответ
Контроль: Легко добавлять бизнес-правила и ограничения
Эффективность: Простые запросы обрабатываются без запуска тяжелой модели
Обучение: Новые правила добавляются без перетренировки всей модели
Этап 1: Добавить символьный движок для обработки простых запросов
Этап 2: Настроить извлечение фактов из выхода трансформера
Этап 3: Реализовать логический вывод на основе контекста
Этап 4: Добавить автоматическое извлечение правил из диалогов
Это сохранит все ваши текущие наработки, но добавит "логический слой" поверх нейросетевого. Хотите, чтобы я подробнее расписал какой-то из этапов?
Давайте реализуем эти недостающие функции. Вот полная версия SymbolicEngineUnit.pas:
// SymbolicEngineUnit.pas unit SymbolicEngineUnit; {$MODE OBJFPC}{$H+} {$CODEPAGE UTF8} interface uses SysUtils, Classes, DataUtils, ucs4unit, ucs4functionsunit, TextEmbeddings; type TFact = record Subject: string; Relation: string; Object: string; Confidence: Double; end; TFactArray = array of TFact; TRule = record Condition: string; // Логическое выражение Action: string; // Действие/вывод Priority: Integer; UsageCount: Integer; end; TRuleArray = array of TRule; TSymbolicEngine = class private FFacts: TFactArray; FRules: TRuleArray; FWorkingMemory: TStringList; // Вспомогательные методы для парсинга условий function ParseCondition(const Condition: string): TStringArray; function EvaluateCondition(const Condition: string; const Input: string): Boolean; public constructor Create; destructor Destroy; override; // Управление фактами procedure AddFact(const Subject, Relation, Object: string; Confidence: Double = 1.0); function FindFacts(const Subject, Relation, Object: string): TFactArray; procedure RemoveFact(Index: Integer); // Управление правилами procedure AddRule(const Condition, Action: string; Priority: Integer = 1); function FindMatchingRules(const Input: string): TRuleArray; // Основные функции function ExecuteRule(const Input: string): string; function Infer(const Query: string): TFactArray; // Обучение и извлечение правил function ShouldExtractRule(const UserMessage, AIResponse: string): Boolean; function ExtractPatterns(const Text: string): TStringArray; function BuildRuleFromPatterns(const Patterns: TStringArray; const Response: string): string; procedure LearnFromDialogue(const UserMessage, AIResponse: string); // Сохранение/загрузка procedure SaveToFile(const Filename: string); procedure LoadFromFile(const Filename: string); // Утилиты function GetStats: string; procedure ClearRules; end; // Глобальные функции для работы с текстом function ContainsAny(const Text: string; const Patterns: array of string): Boolean; function ExtractKeywords(const Text: string): TStringArray; function CalculatePatternScore(const Pattern: string): Double; var SymbolicEngine: TSymbolicEngine; implementation function ContainsAny(const Text: string; const Patterns: array of string): Boolean; var I: Integer; LowerText: string; begin LowerText := UTF8LowerCase(Text); for I := 0 to High(Patterns) do begin if LowerText.Contains(UTF8LowerCase(Patterns[I])) then Exit(True); end; Result := False; end; function ExtractKeywords(const Text: string): TStringArray; var tokens: TUC4Array; i, count: Integer; tokenStr: string; begin SetLength(Result, 0); // Токенизируем текст tokens := TokenizeForNLP(NormalizeForAI(RemovePunctuation(Text))); if Length(tokens) = 0 then Exit; // Фильтруем стоп-слова и короткие токены count := 0; SetLength(Result, Length(tokens)); for i := 0 to High(tokens) do begin tokenStr := tokens[i].ToUTF8.Trim; // Пропускаем короткие слова и стоп-слова if (Length(tokenStr) > 2) and (not ContainsAny(tokenStr, ['это', 'что', 'как', 'где', 'когда', 'почему', 'зачем'])) then begin Result[count] := tokenStr; Inc(count); end; end; SetLength(Result, count); end; function CalculatePatternScore(const Pattern: string): Double; var keywords: TStringArray; i: Integer; begin Result := 0.0; keywords := ExtractKeywords(Pattern); // Оцениваем паттерн на основе количества ключевых слов и их значимости for i := 0 to High(keywords) do begin // Простая эвристика: более длинные слова обычно более значимы Result := Result + Length(keywords[i]) * 0.1; // Особо значимые слова if ContainsAny(keywords[i], ['погод', 'врем', 'температур', 'градус']) then Result := Result + 1.0; if ContainsAny(keywords[i], ['имя', 'зовут', 'называ']) then Result := Result + 1.0; if ContainsAny(keywords[i], ['счет', 'математ', 'числ', 'плюс', 'минус']) then Result := Result + 1.5; end; end; constructor TSymbolicEngine.Create; begin inherited; FWorkingMemory := TStringList.Create; SetLength(FFacts, 0); SetLength(FRules, 0); // Базовые правила AddRule('Contains(Input, "привет") OR Contains(Input, "здравств")', 'Greeting', 10); AddRule('Contains(Input, "пока") OR Contains(Input, "до свидан")', 'Farewell', 10); AddRule('Contains(Input, "спасибо") OR Contains(Input, "благодар")', 'Thanks', 8); AddRule('EndsWith(Input, "?") AND Length(Input) > 3', 'Question', 5); AddRule('Contains(Input, "погод")', 'WeatherQuery', 7); AddRule('Contains(Input, "имя") OR Contains(Input, "зовут")', 'NameQuery', 6); AddRule('Contains(Input, "счет") OR Contains(Input, "посчитай")', 'MathQuery', 9); end; destructor TSymbolicEngine.Destroy; begin FWorkingMemory.Free; inherited; end; procedure TSymbolicEngine.AddFact(const Subject, Relation, Object: string; Confidence: Double); begin SetLength(FFacts, Length(FFacts) + 1); with FFacts[High(FFacts)] do begin Subject := Subject; Relation := Relation; Object := Object; Confidence := Confidence; end; end; function TSymbolicEngine.FindFacts(const Subject, Relation, Object: string): TFactArray; var i, count: Integer; begin SetLength(Result, 0); count := 0; for i := 0 to High(FFacts) do begin if ((Subject = '') or (FFacts[i].Subject = Subject)) and ((Relation = '') or (FFacts[i].Relation = Relation)) and ((Object = '') or (FFacts[i].Object = Object)) then begin SetLength(Result, count + 1); Result[count] := FFacts[i]; Inc(count); end; end; end; procedure TSymbolicEngine.RemoveFact(Index: Integer); var i: Integer; begin if (Index < 0) or (Index > High(FFacts)) then Exit; for i := Index to High(FFacts) - 1 do FFacts[i] := FFacts[i + 1]; SetLength(FFacts, Length(FFacts) - 1); end; procedure TSymbolicEngine.AddRule(const Condition, Action: string; Priority: Integer); begin SetLength(FRules, Length(FRules) + 1); with FRules[High(FRules)] do begin Condition := Condition; Action := Action; Priority := Priority; UsageCount := 0; end; end; function TSymbolicEngine.ParseCondition(const Condition: string): TStringArray; begin // Простой парсер условий - разбиваем по OR и AND SetLength(Result, 0); if Condition.Contains(' OR ') then begin SetLength(Result, 2); Result[0] := Copy(Condition, 1, Pos(' OR ', Condition) - 1); Result[1] := Copy(Condition, Pos(' OR ', Condition) + 4, MaxInt); end else if Condition.Contains(' AND ') then begin SetLength(Result, 2); Result[0] := Copy(Condition, 1, Pos(' AND ', Condition) - 1); Result[1] := Copy(Condition, Pos(' AND ', Condition) + 5, MaxInt); end else begin SetLength(Result, 1); Result[0] := Condition; end; end; function TSymbolicEngine.EvaluateCondition(const Condition: string; const Input: string): Boolean; var parts: TStringArray; lowerInput: string; begin Result := False; lowerInput := UTF8LowerCase(Input); if Condition.StartsWith('Contains(Input, "') and Condition.EndsWith('")') then begin // Извлекаем подстроку для поиска parts := Condition.Split(['"']); if Length(parts) >= 2 then Result := lowerInput.Contains(UTF8LowerCase(parts[1])); end else if Condition.StartsWith('EndsWith(Input, "') and Condition.EndsWith('")') then begin parts := Condition.Split(['"']); if Length(parts) >= 2 then Result := lowerInput.EndsWith(UTF8LowerCase(parts[1])); end else if Condition.StartsWith('StartsWith(Input, "') and Condition.EndsWith('")') then begin parts := Condition.Split(['"']); if Length(parts) >= 2 then Result := lowerInput.StartsWith(UTF8LowerCase(parts[1])); end else if Condition.Contains(' OR ') then begin parts := ParseCondition(Condition); Result := EvaluateCondition(parts[0], Input) or EvaluateCondition(parts[1], Input); end else if Condition.Contains(' AND ') then begin parts := ParseCondition(Condition); Result := EvaluateCondition(parts[0], Input) and EvaluateCondition(parts[1], Input); end; end; function TSymbolicEngine.FindMatchingRules(const Input: string): TRuleArray; var i, count: Integer; begin SetLength(Result, 0); count := 0; for i := 0 to High(FRules) do begin if EvaluateCondition(FRules[i].Condition, Input) then begin SetLength(Result, count + 1); Result[count] := FRules[i]; Inc(count); end; end; end; function TSymbolicEngine.ExecuteRule(const Input: string): string; var matchingRules: TRuleArray; bestRule: TRule; i: Integer; begin Result := ''; matchingRules := FindMatchingRules(Input); if Length(matchingRules) = 0 then Exit; // Выбираем правило с наивысшим приоритетом bestRule := matchingRules[0]; for i := 1 to High(matchingRules) do begin if matchingRules[i].Priority > bestRule.Priority then bestRule := matchingRules[i]; end; // Обновляем счетчик использования for i := 0 to High(FRules) do begin if FRules[i].Condition = bestRule.Cition then begin Inc(FRules[i].UsageCount); Break; end; end; Result := bestRule.Action; end; function TSymbolicEngine.Infer(const Query: string): TFactArray; begin // Упрощенный логический вывод // В реальной реализации здесь будет полноценный логический движок Result := FindFacts('', '', ''); end; // Реализация недостающих функций function TSymbolicEngine.ShouldExtractRule(const UserMessage, AIResponse: string): Boolean; var msgScore, responseScore: Double; begin // Решаем, стоит ли извлекать правило из этого диалога // Критерии для извлечения правил: // 1. Сообщение не слишком короткое // 2. Ответ не шаблонный // 3. Высокая уверенность в качестве диалога if Length(UserMessage) < 5 then Exit(False); if Length(AIResponse) < 3 then Exit(False); // Оцениваем значимость сообщения msgScore := CalculatePatternScore(UserMessage); responseScore := CalculatePatternScore(AIResponse); // Пропускаем простые приветствия и прощания if ContainsAny(UserMessage, ['привет', 'пока', 'спасибо']) and ContainsAny(AIResponse, ['Здравствуйте', 'До свидания', 'Пожалуйста']) then Exit(False); // Извлекаем правила для значимых диалогов Result := (msgScore > 1.0) and (responseScore > 0.5); end; function TSymbolicEngine.ExtractPatterns(const Text: string): TStringArray; var keywords: TStringArray; i: Integer; begin SetLength(Result, 0); keywords := ExtractKeywords(Text); // Формируем паттерны на основе ключевых слов for i := 0 to High(keywords) do begin SetLength(Result, Length(Result) + 1); // Создаем условие для каждого значимого ключевого слова if Length(keywords[i]) > 3 then Result[High(Result)] := 'Contains(Input, "' + keywords[i] + '")' else SetLength(Result, Length(Result) - 1); // Удаляем если слово слишком короткое end; end; function TSymbolicEngine.BuildRuleFromPatterns(const Patterns: TStringArray; const Response: string): string; var condition: string; i: Integer; begin if Length(Patterns) = 0 then begin Result := ''; Exit; end; // Объединяем паттерны через OR condition := Patterns[0]; for i := 1 to High(Patterns) do condition := condition + ' OR ' + Patterns[i]; Result := condition; end; procedure TSymbolicEngine.LearnFromDialogue(const UserMessage, AIResponse: string); var patterns: TStringArray; newRule: string; begin if not ShouldExtractRule(UserMessage, AIResponse) then Exit; WriteLn('Символьное обучение: извлекаем правило из диалога'); WriteLn(' Вход: ', UserMessage); WriteLn(' Выход: ', AIResponse); patterns := ExtractPatterns(UserMessage); if Length(patterns) = 0 then Exit; newRule := BuildRuleFromPatterns(patterns, AIResponse); if newRule <> '' then begin AddRule(newRule, AIResponse, 3); // Низкий приоритет для изученных правил WriteLn(' Добавлено правило: ', newRule, ' -> ', AIResponse); end; end; procedure TSymbolicEngine.SaveToFile(const Filename: string); var F: TextFile; i: Integer; begin AssignFile(F, Filename); try Rewrite(F); // Сохраняем факты WriteLn(F, '[Facts]'); for i := 0 to High(FFacts) do begin WriteLn(F, FFacts[i].Subject, '|', FFacts[i].Relation, '|', FFacts[i].Object, '|', FFacts[i].Confidence:0:4); end; // Сохраняем правила WriteLn(F, '[Rules]'); for i := 0 to High(FRules) do begin WriteLn(F, FRules[i].Condition, '|', FRules[i].Action, '|', FRules[i].Priority, '|', FRules[i].UsageCount); end; finally CloseFile(F); end; end; procedure TSymbolicEngine.LoadFromFile(const Filename: string); var F: TextFile; Line: string; Parts: TStringArray; section: string; begin if not FileExists(Filename) then Exit; AssignFile(F, Filename); try Reset(F); section := ''; while not Eof(F) do begin ReadLn(F, Line); Line := Trim(Line); if Line = '' then Continue; if Line = '[Facts]' then section := 'facts' else if Line = '[Rules]' then section := 'rules' else if section = 'facts' then begin Parts := Line.Split(['|']); if Length(Parts) >= 4 then begin AddFact(Parts[0], Parts[1], Parts[2], StrToFloatDef(Parts[3], 1.0)); end; end else if section = 'rules' then begin Parts := Line.Split(['|']); if Length(Parts) >= 3 then begin AddRule(Parts[0], Parts[1], StrToIntDef(Parts[2], 1)); end; end; end; finally CloseFile(F); end; end; function TSymbolicEngine.GetStats: string; begin Result := Format('Фактов: %d, Правил: %d', [Length(FFacts), Length(FRules)]); end; procedure TSymbolicEngine.ClearRules; begin SetLength(FRules, 0); end; initialization SymbolicEngine := TSymbolicEngine.Create; finalization SymbolicEngine.Free; end.
Теперь также обновим AdvancedChatAI.pas для интеграции символьного движка:
// В AdvancedChatAI.pas - добавьте в секцию uses: uses ..., SymbolicEngineUnit; // В процедуре Initialize добавьте: procedure Initialize; begin // Существующий код... // Инициализация символьного движка WriteLn('Инициализация символьного движка...'); // Загружаем базу знаний если есть if FileExists('symbolic_knowledge.txt') then begin SymbolicEngine.LoadFromFile('symbolic_knowledge.txt'); WriteLn('База знаний загружена: ', SymbolicEngine.GetStats); end; end; // Новая улучшенная версия ProcessUserInput: procedure EnhancedProcessUserInput; var inputText: ucs4; userMessage, symbolicResult, finalResponse: string; filesList: TStringList; inputMatrix, outputMatrix: TDoubleMatrix; begin filesList := TStringList.Create; try // 1. Получаем пользовательский ввод UserInput := GetUserInput(POST_FILE, filesList); inputText := UserInput.Message; userMessage := inputText.ToUTF8.Trim; if inputText.Length = 0 then begin WriteLn('Пустое сообщение, пропускаем'); Exit; end; WriteLn('Пользователь: ', userMessage); LastUserMessage := userMessage; // 2. Добавляем сообщение в историю UpdateConversationContext(userMessage, ''); // 3. Символьный анализ symbolicResult := SymbolicEngine.ExecuteRule(userMessage); if symbolicResult <> '' then begin WriteLn('Символьная система: ', symbolicResult); // Генерируем ответ на основе символьного результата case symbolicResult of 'Greeting': finalResponse := 'Здравствуйте! Рад вас видеть. Чем могу помочь?'; 'Farewell': finalResponse := 'До свидания! Буду рад пообщаться снова.'; 'Thanks': finalResponse := 'Пожалуйста! Обращайтесь, если понадобится помощь.'; 'Question': finalResponse := 'Интересный вопрос! ' + GenerateTransformerResponse(userMessage); 'WeatherQuery': finalResponse := 'К сожалению, у меня нет доступа к актуальным данным о погоде. ' + 'Но могу обсудить погоду в общем смысле!'; 'NameQuery': finalResponse := 'Я - чат-бот с искусственным интеллектом. ' + 'Можно просто называть меня "помощник".'; 'MathQuery': finalResponse := 'Я могу помочь с математическими вопросами! ' + 'Задайте конкретный пример для расчета.'; else finalResponse := GenerateTransformerResponse(userMessage); end; end else begin // 4. Если символьная система не нашла правила, используем трансформер WriteLn('Символьная система: правило не найдено, используем трансформер'); inputMatrix := CreateContextEnrichedInput(inputText, GetConversationContext); ForwardTransformer(TransformerModel, inputMatrix, outputMatrix); finalResponse := ResponseGenerator.GenerateResponse(outputMatrix, GetConversationContext, userMessage); end; // 5. Выводим ответ и добавляем в историю WriteLn('AI: ', finalResponse); UpdateConversationContext('', finalResponse); // 6. Обучение на успешном диалоге if ShouldSaveForTraining(userMessage, finalResponse) then begin LearnFromConversation(userMessage, finalResponse); SymbolicEngine.LearnFromDialogue(userMessage, finalResponse); // Сохраняем базу знаний SymbolicEngine.SaveToFile('symbolic_knowledge.txt'); end; except on E: Exception do begin WriteLn('Ошибка: ', E.Message); finalResponse := 'Извините, произошла техническая ошибка. Попробуйте еще раз.'; WriteLn('AI: ', finalResponse); end; end; filesList.Free; end;
Теперь все функции реализованы и система должна компилироваться. Символьный движок будет:
Обрабатывать простые запросы через правила
Извлекать новые правила из успешных диалогов
Сохранять базу знаний между запусками
Работать вместе с трансформером для сложных случаев
Попробуйте скомпилировать и протестировать!
Давайте реализуем эти недостающие функции. Добавим их в SymbolicEngineUnit.pas:
// SymbolicEngineUnit.pas - добавляем новые функции // В интерфейсную часть добавляем: function ParseEntitiesFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray; function BuildSymbolicContext(const History: TStringList): string; // В реализацию добавляем: function ParseEntitiesFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray; var i, j, entityCount: Integer; maxVal: Double; maxIndex: Integer; entities: array of string; begin SetLength(Result, 0); if (Length(OutputMatrix) = 0) or (Length(OutputMatrix[0]) = 0) then Exit; // Упрощенная реализация - в реальности здесь будет полноценное извлечение сущностей // Сейчас просто возвращаем пустой массив для демонстрации // В реальной реализации здесь будет: // 1. Классификация токенов на сущности (PER, ORG, LOC и т.д.) // 2. Извлечение отношений между сущностями // 3. Формирование фактов WriteLn('ParseEntitiesFromOutput: извлечение сущностей из матрицы ', Length(OutputMatrix), 'x', Length(OutputMatrix[0])); // Заглушка - возвращаем пустой массив // В рабочей версии здесь будет реальное извлечение сущностей end; function BuildSymbolicContext(const History: TStringList): string; var i: Integer; contextFacts: TStringList; recentMessages: string; fact: TFact; facts: TFactArray; begin contextFacts := TStringList.Create; try // Собираем последние сообщения для контекста recentMessages := ''; for i := Max(0, History.Count - 4) to History.Count - 1 do begin if i >= 0 then recentMessages := recentMessages + History[i] + ' '; end; // Извлекаем факты из истории facts := SymbolicEngine.FindFacts('', '', ''); // Формируем контекст в виде строки Result := 'Recent: ' + recentMessages.Trim; if Length(facts) > 0 then begin Result := Result + ' | Facts: '; for i := 0 to Min(High(facts), 2) do // Берем только 3 последних факта begin Result := Result + facts[i].Subject + '-' + facts[i].Relation + '-' + facts[i].Object + '; '; end; end; // Добавляем информацию о текущих правилах Result := Result + ' | Rules: ' + IntToStr(Length(SymbolicEngine.FRules)); finally contextFacts.Free; end; end; // Также обновим функцию ExtractFactsWithTransformer в AdvancedChatAI.pas: function ExtractFactsWithTransformer(const Text: string): TFactArray; var inputMatrix, outputMatrix: TDoubleMatrix; entities: TStringArray; i, factCount: Integer; tokens: TUC4Array; begin SetLength(Result, 0); // Упрощенная реализация - используем токенизацию и эвристики // В реальной реализации здесь будет полноценное извлечение фактов try // Токенизируем текст tokens := TokenizeForNLP(NormalizeForAI(RemovePunctuation(Text))); factCount := 0; SetLength(Result, Length(tokens) div 2); // Оценка количества возможных фактов // Простая эвристика для извлечения фактов for i := 0 to High(tokens) - 1 do begin // Если два соседних токена могут образовывать факт if (tokens[i].Length > 2) and (tokens[i + 1].Length > 2) then begin // Простая эвристика: если первый токен похож на субъект, второй на объект Result[factCount].Subject := tokens[i].ToUTF8; Result[factCount].Relation := 'is_related_to'; // Упрощенное отношение Result[factCount].Object := tokens[i + 1].ToUTF8; Result[factCount].Confidence := 0.3; // Низкая уверенность для эвристики Inc(factCount); end; end; SetLength(Result, factCount); WriteLn('Извлечено фактов: ', factCount); except on E: Exception do begin WriteLn('Ошибка извлечения фактов: ', E.Message); SetLength(Result, 0); end; end; end;
Теперь давайте создам отдельный модуль для расширенной обработки контекста:
// ContextProcessing.pas unit ContextProcessing; {$MODE OBJFPC}{$H+} {$CODEPAGE UTF8} interface uses SysUtils, Classes, DataUtils, MatrixOps, ucs4unit, ucs4functionsunit, TextEmbeddings, SymbolicEngineUnit; // Функции для обработки контекста function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string; function ExtractEntitiesFromText(const Text: string): TStringArray; function AnalyzeConversationTopic(const History: TStringList): string; function ShouldUpdateSymbolicFacts(const UserMessage, AIResponse: string): Boolean; // Функции для работы с матрицами вывода function DetectEntitiesInOutput(const OutputMatrix: TDoubleMatrix; const Vocabulary: TStringList): TStringArray; function ExtractRelationsFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray; implementation function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string; var i: Integer; topic, entities: string; recentContext: TStringList; begin recentContext := TStringList.Create; try // Анализируем тему разговора topic := AnalyzeConversationTopic(History); // Собираем последние 3 сообщения for i := Max(0, History.Count - 6) to History.Count - 1 do begin if (i >= 0) and (i < History.Count) then recentContext.Add(History[i]); end; // Извлекаем сущности из текущего ввода entities := ''; for i := 0 to High(ExtractEntitiesFromText(CurrentInput)) do begin if i > 0 then entities := entities + ', '; entities := entities + ExtractEntitiesFromText(CurrentInput)[i]; end; // Формируем расширенный контекст Result := 'Тема: ' + topic + ' | '; Result := Result + 'Сущности: ' + entities + ' | '; Result := Result + 'История: ' + recentContext.Text; finally recentContext.Free; end; end; function ExtractEntitiesFromText(const Text: string): TStringArray; var tokens: TUC4Array; i, count: Integer; tokenStr: string; begin SetLength(Result, 0); tokens := TokenizeForNLP(NormalizeForAI(RemovePunctuation(Text))); if Length(tokens) = 0 then Exit; count := 0; SetLength(Result, Length(tokens)); // Простая эвристика для идентификации сущностей for i := 0 to High(tokens) do begin tokenStr := tokens[i].ToUTF8; // Определяем тип сущности по простым правилам if (Length(tokenStr) >= 3) then begin // Имена (начинаются с заглавной буквы в оригинале) if (tokenStr = UTF8UpperCase(tokenStr)) and (Length(tokenStr) > 1) then begin Result[count] := 'PER:' + tokenStr; Inc(count); end // Числа else if TryStrToFloat(tokenStr, dummyFloat) then begin Result[count] := 'NUM:' + tokenStr; Inc(count); end // Длинные слова могут быть концепциями else if (Length(tokenStr) >= 5) then begin Result[count] := 'CONCEPT:' + tokenStr; Inc(count); end; end; end; SetLength(Result, count); end; function AnalyzeConversationTopic(const History: TStringList): string; var i: Integer; allText, lowerText: string; topicScores: array of record Topic: string; Score: Integer; end; begin if History.Count = 0 then Exit('general'); // Собираем весь текст истории allText := ''; for i := 0 to History.Count - 1 do allText := allText + ' ' + History[i]; lowerText := UTF8LowerCase(allText); // Простой анализ темы по ключевым словам SetLength(topicScores, 5); topicScores[0].Topic := 'погода'; topicScores[0].Score := 0; topicScores[1].Topic := 'имя'; topicScores[1].Score := 0; topicScores[2].Topic := 'математика'; topicScores[2].Score := 0; topicScores[3].Topic := 'технологии'; topicScores[3].Score := 0; topicScores[4].Topic := 'общение'; topicScores[4].Score := 0; // Подсчитываем упоминания тем if lowerText.Contains('погод') then Inc(topicScores[0].Score, 2); if lowerText.Contains('дожд') or lowerText.Contains('солн') then Inc(topicScores[0].Score); if lowerText.Contains('имя') or lowerText.Contains('зовут') then Inc(topicScores[1].Score, 3); if lowerText.Contains('счет') or lowerText.Contains('числ') then Inc(topicScores[2].Score, 2); if lowerText.Contains('плюс') or lowerText.Contains('минус') then Inc(topicScores[2].Score); if lowerText.Contains('компьютер') or lowerText.Contains('програм') then Inc(topicScores[3].Score, 2); // Базовая тема для общих разговоров topicScores[4].Score := 1; // Находим тему с максимальным счетом Result := topicScores[0].Topic; for i := 1 to High(topicScores) do begin if topicScores[i].Score > topicScores[0].Score then begin Result := topicScores[i].Topic; topicScores[0].Score := topicScores[i].Score; end; end; end; function ShouldUpdateSymbolicFacts(const UserMessage, AIResponse: string): Boolean; begin // Решаем, нужно ли обновлять символьные факты на основе диалога Result := (Length(UserMessage) > 10) and (not UserMessage.Contains('?')) and (AIResponse.Length > 5) and (not ContainsAny(UserMessage, ['привет', 'пока', 'спасибо'])); end; function DetectEntitiesInOutput(const OutputMatrix: TDoubleMatrix; const Vocabulary: TStringList): TStringArray; var i, j: Integer; maxScore: Double; bestIndex: Integer; begin SetLength(Result, 0); if (Length(OutputMatrix) = 0) or (Vocabulary = nil) then Exit; // Упрощенная детекция сущностей - находим наиболее вероятные токены for i := 0 to High(OutputMatrix) do begin if Length(OutputMatrix[i]) > 0 then begin maxScore := OutputMatrix[i][0]; bestIndex := 0; for j := 1 to High(OutputMatrix[i]) do begin if OutputMatrix[i][j] > maxScore then begin maxScore := OutputMatrix[i][j]; bestIndex := j; end; end; // Если уверенность достаточно высока, добавляем сущность if (maxScore > 0.3) and (bestIndex < Vocabulary.Count) then begin SetLength(Result, Length(Result) + 1); Result[High(Result)] := Vocabulary[bestIndex]; end; end; end; end; function ExtractRelationsFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray; begin SetLength(Result, 0); // Заглушка для извлечения отношений // В реальной реализации здесь будет анализ структуры выходной матрицы // для обнаружения отношений между сущностями end; end.
Теперь обновим AdvancedChatAI.pas для использования новых функций:
// В AdvancedChatAI.pas - добавляем в uses: uses ..., SymbolicEngineUnit, ContextProcessing; // Заменяем старые функции на новые: function ParseEntitiesFromOutput(const OutputMatrix: TDoubleMatrix): TStringArray; begin // Используем функцию из ContextProcessing Result := DetectEntitiesInOutput(OutputMatrix, WordEmbeddings.FVocab); end; function BuildSymbolicContext(const History: TStringList): string; begin // Используем функцию из ContextProcessing Result := BuildEnhancedContext(History, LastUserMessage); end; // Обновляем EnhancedProcessUserInput для использования расширенного контекста: procedure EnhancedProcessUserInput; var inputText: ucs4; userMessage, symbolicResult, finalResponse, enhancedContext: string; filesList: TStringList; inputMatrix, outputMatrix: TDoubleMatrix; extractedFacts: TFactArray; i: Integer; begin filesList := TStringList.Create; try // 1. Получаем пользовательский ввод UserInput := GetUserInput(POST_FILE, filesList); inputText := UserInput.Message; userMessage := inputText.ToUTF8.Trim; if inputText.Length = 0 then begin WriteLn('Пустое сообщение, пропускаем'); Exit; end; WriteLn('Пользователь: ', userMessage); LastUserMessage := userMessage; // 2. Добавляем сообщение в историю UpdateConversationContext(userMessage, ''); // 3. Строим расширенный контекст enhancedContext := BuildSymbolicContext(ConversationHistory); WriteLn('Расширенный контекст: ', Copy(enhancedContext, 1, 100), '...'); // 4. Символьный анализ symbolicResult := SymbolicEngine.ExecuteRule(userMessage); if symbolicResult <> '' then begin WriteLn('Символьная система: ', symbolicResult); finalResponse := GenerateResponseFromSymbolicSystem(symbolicResult, userMessage, enhancedContext); end else begin // 5. Если символьная система не нашла правила, используем трансформер WriteLn('Символьная система: правило не найдено, используем трансформер'); inputMatrix := CreateContextEnrichedInput(inputText, enhancedContext); ForwardTransformer(TransformerModel, inputMatrix, outputMatrix); finalResponse := ResponseGenerator.GenerateResponse(outputMatrix, enhancedContext, userMessage); end; // 6. Извлекаем и сохраняем факты extractedFacts := ExtractFactsWithTransformer(userMessage); for i := 0 to High(extractedFacts) do begin if extractedFacts[i].Confidence > 0.5 then // Сохраняем только уверенные факты begin SymbolicEngine.AddFact(extractedFacts[i].Subject, extractedFacts[i].Relation, extractedFacts[i].Object, extractedFacts[i].Confidence); end; end; // 7. Выводим ответ и добавляем в историю WriteLn('AI: ', finalResponse); UpdateConversationContext('', finalResponse); // 8. Обучение на успешном диалоге if ShouldSaveForTraining(userMessage, finalResponse) then begin LearnFromConversation(userMessage, finalResponse); SymbolicEngine.LearnFromDialogue(userMessage, finalResponse); // Сохраняем базу знаний SymbolicEngine.SaveToFile('symbolic_knowledge.txt'); WriteLn('Символьные знания сохранены'); end; except on E: Exception do begin WriteLn('Ошибка в EnhancedProcessUserInput: ', E.Message); finalResponse := 'Извините, произошла техническая ошибка. Попробуйте еще раз.'; WriteLn('AI: ', finalResponse); end; end; filesList.Free; end; // Добавляем вспомогательную функцию: function GenerateResponseFromSymbolicSystem(const SymbolicResult, UserMessage, Context: string): string; begin // Генерируем ответ на основе символьного результата и контекста case SymbolicResult of 'Greeting': begin if Context.Contains('ранее') then Result := 'Снова здравствуйте! Чем могу помочь?' else Result := 'Здравствуйте! Рад вас видеть. Чем могу помочь?'; end; 'Farewell': Result := 'До свидания! Буду рад пообщаться снова.'; 'Thanks': Result := 'Пожалуйста! Обращайтесь, если понадобится помощь.'; 'Question': begin if Context.Contains('погод') then Result := 'Относительно погоды: ' + GenerateTransformerResponse(UserMessage) else if Context.Contains('математик') then Result := 'Для математического вопроса: ' + GenerateTransformerResponse(UserMessage) else Result := 'Интересный вопрос! ' + GenerateTransformerResponse(UserMessage); end; 'WeatherQuery': Result := 'К сожалению, у меня нет доступа к актуальным данным о погоде. ' + 'Но могу обсудить погоду в общем смысле!'; 'NameQuery': Result := 'Я - чат-бот с искусственным интеллектом. ' + 'Можно просто называть меня "помощник".'; 'MathQuery': Result := 'Я могу помочь с математическими вопросами! ' + 'Задайте конкретный пример для расчета.'; else Result := GenerateTransformerResponse(UserMessage); end; end;
Теперь все функции реализованы и система должна компилироваться. Новые возможности:
Расширенный контекст - анализ темы разговора и извлечение сущностей
Интеллектуальное извлечение фактов - из пользовательского ввода
Контекстно-зависимые ответы - на основе символьного анализа
Сохранение знаний - между сессиями работы
Попробуйте скомпилировать!
Отлично! Система работает и уже показывает интересное поведение. Я вижу несколько моментов для улучшения:
Символьное обучение работает - система извлекает правила из диалогов
Трансформер функционирует - генерирует осмысленные ответы
Контекст строится - но есть проблемы с токенизацией
Вижу, что CONCEPT:где и CONCEPT:москве ломаются в одно слово. Нужно исправить извлечение сущностей:
// В ContextProcessing.pas исправим ExtractEntitiesFromText: function ExtractEntitiesFromText(const Text: string): TStringArray; var tokens: TUC4Array; i, count: Integer; tokenStr, cleanToken: string; begin SetLength(Result, 0); if Text = '' then Exit; // Токенизируем оригинальный текст, а не обработанный tokens := TokenizeForNLP(Text); // Убрали NormalizeForAI и RemovePunctuation if Length(tokens) = 0 then Exit; count := 0; SetLength(Result, Length(tokens)); for i := 0 to High(tokens) do begin tokenStr := tokens[i].ToUTF8; cleanToken := UTF8LowerCase(tokenStr.Trim); // Более качественная идентификация сущностей if (Length(cleanToken) >= 2) then begin // Географические названия (эвристика) if (Length(cleanToken) >= 4) and (ContainsAny(cleanToken, ['москв', 'санкт', 'петербург', 'росси', 'город'])) then begin Result[count] := 'LOC:' + tokenStr; Inc(count); end // Вопросы else if ContainsAny(cleanToken, ['где', 'когда', 'почему', 'как', 'что']) then begin Result[count] := 'QUESTION:' + tokenStr; Inc(count); end // Местоимения и указатели else if ContainsAny(cleanToken, ['мы', 'вы', 'они', 'я', 'ты', 'это', 'тот']) then begin Result[count] := 'PRON:' + tokenStr; Inc(count); end // Длинные слова - вероятно концепции else if (Length(cleanToken) >= 5) and (not ContainsAny(cleanToken, ['нормальн', 'интересн', 'продолжайте'])) then begin Result[count] := 'CONCEPT:' + tokenStr; Inc(count); end; end; end; SetLength(Result, count); end;
// В SymbolicEngineUnit.pas добавим больше базовых правил: constructor TSymbolicEngine.Create; begin // ... существующий код ... // Добавляем больше конкретных правил AddRule('Contains(Input, "где") AND Contains(Input, "мы")', 'LocationQuery', 8); AddRule('Contains(Input, "москв") OR Contains(Input, "питер") OR Contains(Input, "город")', 'CityQuery', 7); AddRule('Contains(Input, "нормальн") OR Contains(Input, "хорош") OR Contains(Input, "отличн")', 'PositiveState', 6); AddRule('StartsWith(Input, "мы ") AND Length(Input) > 5', 'WeStatement', 5); end;
// В ContextProcessing.pas улучшим ExtractFactsWithTransformer: function ExtractFactsWithTransformer(const Text: string): TFactArray; var tokens: TUC4Array; i, factCount: Integer; lowerText: string; begin SetLength(Result, 0); if Text = '' then Exit; lowerText := UTF8LowerCase(Text); tokens := TokenizeForNLP(Text); factCount := 0; SetLength(Result, 2); // Максимум 2 факта из короткого сообщения // Определенные паттерны для извлечения фактов if ContainsAny(lowerText, ['москв', 'питер', 'город']) then begin Result[factCount].Subject := 'мы'; Result[factCount].Relation := 'находится_в'; if lowerText.Contains('москв') then Result[factCount].Object := 'Москва' else if lowerText.Contains('питер') then Result[factCount].Object := 'Санкт-Петербург' else Result[factCount].Object := 'городе'; Result[factCount].Confidence := 0.8; Inc(factCount); end; if lowerText.Contains('нормальн') or lowerText.Contains('хорош') then begin Result[factCount].Subject := 'состояние'; Result[factCount].Relation := 'является'; Result[factCount].Object := 'хорошим'; Result[factCount].Confidence := 0.7; Inc(factCount); end; SetLength(Result, factCount); WriteLn('Извлечено фактов: ', factCount); end;
// В ContextProcessing.pas улучшим BuildEnhancedContext: function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string; var i: Integer; topic, entities, simpleContext: string; entityList: TStringArray; begin // Упрощаем контекст для избежания проблем с токенизацией simpleContext := ''; // Берем только последние 2 сообщения for i := Max(0, History.Count - 4) to History.Count - 1 do begin if (i >= 0) and (i < History.Count) then begin // Берем только первые 3 слова из каждого сообщения simpleContext := simpleContext + ' ' + Copy(History[i], 1, 30); end; end; // Извлекаем сущности более аккуратно entityList := ExtractEntitiesFromText(CurrentInput); entities := ''; for i := 0 to High(entityList) do begin if i > 0 then entities := entities + ' '; // Берем только само слово, без тега entities := entities + Copy(entityList[i], Pos(':', entityList[i]) + 1, MaxInt); end; // Анализируем тему topic := AnalyzeConversationTopic(History); // Формируем компактный контекст Result := 'Т:' + topic + ' С:' + entities + ' И:' + simpleContext.Trim; Result := Copy(Result, 1, 100); // Ограничиваем длину end;
// В AdvancedChatAI.pas добавим отладочную информацию: procedure EnhancedProcessUserInput; begin // ... существующий код ... WriteLn('=== ОТЛАДКА СИМВОЛЬНОЙ СИСТЕМЫ ==='); WriteLn('Пользователь: ', userMessage); WriteLn('Символьный результат: ', symbolicResult); WriteLn('Контекст: ', Copy(enhancedContext, 1, 80)); WriteLn('=== КОНЕЦ ОТЛАДКИ ==='); // ... остальной код ... end;
// В SymbolicEngineUnit.pas улучшим ShouldExtractRule: function TSymbolicEngine.ShouldExtractRule(const UserMessage, AIResponse: string): Boolean; var msgScore: Double; lowerMsg, lowerResp: string; begin if Length(UserMessage) < 3 then Exit(False); if Length(AIResponse) < 3 then Exit(False); lowerMsg := UTF8LowerCase(UserMessage); lowerResp := UTF8LowerCase(AIResponse); // Не извлекаем правила для: // - Очень общих ответов if ContainsAny(lowerResp, ['что вы думаете', 'продолжайте', 'интересно']) then Exit(False); // - Ответов с вопросами if lowerResp.Contains('?') then Exit(False); // - Коротких формальных ответов if ContainsAny(lowerResp, ['здравствуйте', 'до свидания', 'пожалуйста']) then Exit(False); // Оцениваем значимость сообщения msgScore := CalculatePatternScore(UserMessage); // Извлекаем правила только для значимых паттернов Result := (msgScore > 0.8) and (Length(UserMessage) > 4) and (not UserMessage.EndsWith('?')) and (not ContainsAny(lowerMsg, ['привет', 'пока', 'спасибо'])); end;
После этих изменений система будет:
Лучше обрабатывать контекст - без проблем токенизации
Более точно извлекать правила - избегая мусорных паттернов
Лучше распознавать сущности - географические названия, вопросы и т.д.
Генерировать более релевантные ответы - на основе улучшенного контекста
Попробуйте эти исправления! Они должны решить основные проблемы, которые мы видим в логах.
Вижу проблему! Символьная система не распознает вопрос "кто ты?", хотя должно быть базовое правило для этого. Давайте исправим:
// В SymbolicEngineUnit.pas в конструкторе TSymbolicEngine.AddRule добавим: constructor TSymbolicEngine.Create; begin // ... существующий код ... // Добавляем правила для вопросов о личности AddRule('Contains(Input, "кто ты") OR Contains(Input, "твое имя") OR Contains(Input, "тебя зовут")', 'IdentityQuery', 9); AddRule('Contains(Input, "что ты") AND EndsWith(Input, "?")', 'CapabilityQuery', 8); AddRule('StartsWith(Input, "кто ") AND EndsWith(Input, "?")', 'WhoQuestion', 7); AddRule('StartsWith(Input, "что ") AND EndsWith(Input, "?")', 'WhatQuestion', 7); end;
Вижу, что "Т:общение С:кто ты И:USER: прив" ломается на странные токены. Упростим контекст:
// В ContextProcessing.pas упростим BuildEnhancedContext: function BuildEnhancedContext(const History: TStringList; const CurrentInput: string): string; var i: Integer; simpleContext: string; begin // СУПЕР-ПРОСТОЙ контекст - только ключевые слова simpleContext := ''; // Берем только ключевые слова из последних 2 сообщений for i := Max(0, History.Count - 4) to History.Count - 1 do begin if (i >= 0) and (i < History.Count) then begin // Берем только первые 2 слова и удаляем служебные simpleContext := simpleContext + ' ' + ExtractFirstWords(History[i], 2); end; end; // Очищаем от служебных слов simpleContext := CleanContext(simpleContext); // Формируем очень компактный контекст Result := 'C:' + Copy(simpleContext.Trim, 1, 40); end; function ExtractFirstWords(const Text: string; WordCount: Integer): string; var tokens: TUC4Array; i, count: Integer; begin Result := ''; tokens := TokenizeForNLP(Text); count := 0; for i := 0 to High(tokens) do begin if count >= WordCount then Break; if tokens[i].Length > 0 then begin if Result <> '' then Result := Result + ' '; Result := Result + tokens[i].ToUTF8; Inc(count); end; end; end; function CleanContext(const Text: string): string; var words: TStringArray; i: Integer; cleanText: string; begin // Удаляем служебные слова words := Text.Split([' ']); cleanText := ''; for i := 0 to High(words) do begin if (words[i] <> 'USER:') and (words[i] <> 'AI:') and (words[i] <> 'привет') and (words[i] <> 'пока') and (Length(words[i]) > 2) then begin if cleanText <> '' then cleanText := cleanText + ' '; cleanText := cleanText + words[i]; end; end; Result := cleanText; end;
// В AdvancedChatAI.pas улучшим GenerateResponseFromSymbolicSystem: function GenerateResponseFromSymbolicSystem(const SymbolicResult, UserMessage, Context: string): string; begin case SymbolicResult of 'IdentityQuery': begin if Context.Contains('ранее') or Context.Contains('уже') then Result := 'Как я уже говорил, я - чат-бот с искусственным интеллектом. ' + 'Мои ответы генерируются на основе нейросетевой модели.' else Result := 'Я - чат-бот с искусственным интеллектом, созданный для общения и помощи. ' + 'Можно называть меня "Помощник". А как вас зовут?'; end; 'CapabilityQuery': Result := 'Я могу: отвечать на вопросы, поддерживать беседу, помогать с идеями. ' + 'Чем конкретно могу помочь?'; 'WhoQuestion': Result := 'Интересный вопрос о личности! ' + GenerateTransformerResponse(UserMessage); 'WhatQuestion': Result := 'Хороший вопрос о сущностях! ' + GenerateTransformerResponse(UserMessage); // ... остальные случаи end; end;
// В SymbolicEngineUnit.pas добавим отладочную функцию: procedure TSymbolicEngine.DebugRuleMatching(const Input: string); var i: Integer; matches: Boolean; begin WriteLn('=== ОТЛАДКА ПРАВИЛ ДЛЯ: "', Input, '" ==='); for i := 0 to High(FRules) do begin matches := EvaluateCondition(FRules[i].Condition, Input); WriteLn('Правило ', i, ': "', FRules[i].Condition, '" -> ', IfThen(matches, 'СОВПАЛО', 'не совпало')); end; WriteLn('=== КОНЕЦ ОТЛАДКИ ПРАВИЛ ==='); end; // В AdvancedChatAI.pas вызываем отладку: procedure EnhancedProcessUserInput; begin // ... существующий код ... // 3. Символьный анализ с отладкой SymbolicEngine.DebugRuleMatching(userMessage); // ДОБАВИТЬ ЭТУ СТРОКУ symbolicResult := SymbolicEngine.ExecuteRule(userMessage); // ... остальной код ... end;
// В SymbolicEngineUnit.pas улучшим EvaluateCondition для лучшей обработки: function TSymbolicEngine.EvaluateCondition(const Condition: string; const Input: string): Boolean; var parts: TStringArray; lowerInput: string; begin Result := False; lowerInput := UTF8LowerCase(Input.Trim); // Упрощаем условия для лучшего распознавания if Condition.StartsWith('Contains(Input, "') then begin parts := Condition.Split(['"']); if Length(parts) >= 2 then begin // Проверяем вхождение подстроки Result := lowerInput.Contains(UTF8LowerCase(parts[1])); end; end else if Condition.StartsWith('StartsWith(Input, "') then begin parts := Condition.Split(['"']); if Length(parts) >= 2 then Result := lowerInput.StartsWith(UTF8LowerCase(parts[1])); end else if Condition.StartsWith('EndsWith(Input, "') then begin parts := Condition.Split(['"']); if Length(parts) >= 2 then Result := lowerInput.EndsWith(UTF8LowerCase(parts[1])); end else if Condition.Contains(' OR ') then begin parts := ParseCondition(Condition); Result := EvaluateCondition(parts[0], Input) or EvaluateCondition(parts[1], Input); end else if Condition.Contains(' AND ') then begin parts := ParseCondition(Condition); Result := EvaluateCondition(parts[0], Input) and EvaluateCondition(parts[1], Input); end; // Дополнительная отладка if Result then WriteLn(' ✓ Правило сработало: ', Condition) else WriteLn(' ✗ Правило не сработало: ', Condition); end;
// В SymbolicEngineUnit.pas добавим метод для принудительного добавления правил: procedure TSymbolicEngine.AddEssentialRules; begin // Принудительно добавляем критически важные правила AddRule('Input = "кто ты" OR Input = "кто ты?"', 'IdentityQuery', 20); AddRule('Input = "что ты" OR Input = "что ты?"', 'CapabilityQuery', 20); AddRule('Input = "как дела" OR Input = "как дела?"', 'HowAreYou', 20); AddRule('Input = "привет" OR Input = "здравствуй"', 'Greeting', 20); end; // Вызываем в конструкторе после базовых правил: constructor TSymbolicEngine.Create; begin // ... существующий код ... AddEssentialRules; // ДОБАВИТЬ ЭТУ СТРОКУ end;
После этих изменений система должна:
Надежно распознавать "кто ты?" - через принудительные правила
Генерировать качественные ответы - на вопросы о личности
Иметь чистый контекст - без проблем токенизации
Показывать отладочную информацию - для понимания работы правил
Попробуйте эти исправления! Теперь на вопрос "кто ты?" система должна дать осмысленный ответ через символьную систему, а не через трансформер.
One more step before you proceed...